
--------------------------------------------------------------------

{-
We have seen the datatype for Hand (cardplay):

data Hand = Empty | Add Card Hand

This seems to be a nice solution, that could be of general interest.
In general, when modelling a "collection" of things, we could use
the same principle.

Let us try to define a datatype for lists:

data List = Empty | Add ? List

So, a list is either:

  * Empty
  
  * or it has at least one element (the first), and we model the rest
    of the elements with a list again

What should be on the place of the ? question mark? Answer:
this should be decided by the persion who uses this type, so
we turn this into a parameter.
-}

--------------------------------------------------------------------

-- a datatype for lists
data List a = Empty | Add a (List a)
  deriving ( Show, Eq )

{-
-- we can now model a Hand by using a List
type Hand = List Card
-}

-- tom list checks whether list is empty
tom :: List a -> Bool
tom Empty        = True
tom (Add x list) = False

{-
The type of 'tom', namely

  tom :: List a -> Bool
  
means that it works for ANY type a. Note the difference between type
names starting with a capital:

  Integer, Bool, Float, List, etc.

which denote specific types, and type names with a small letter:

  a, b, c, etc.
  
which denote any type.
-}

-- forst list returns the first element of the list
forst :: List a -> a
forst (Add x list) = x

{-
The type of 'forst', namely

  forst :: List a -> a
  
means that it works for ANY type a. Note that the element type
and the result type should match; they should both be 'a'.
-}

{-
-- alternatively, we can generate an error when the list is empty:
forst :: List a -> a
forst Empty        = error "the list is empty"
forst (Add x list) = x
-}

-- Note: What is the type of 'error'?

-- sist list returns the last element of the list
sist :: List a -> a
sist Empty         = error "the list is empty"
sist (Add x Empty) = x
sist (Add x list)  = sist list -- here, we know that list is non-empty

--------------------------------------------------------------------

{-
There is standard notation in Haskell for the above datatype and
functions.

  []            is used for the empty list ('Empty' above)
  [1,2,3,4,2]   is a list with 5 elements
  [3]           is a list with one element
  x : list      is a list with first element x, and list as the rest
                ('Add x list' above)
  [Integer]     is the type of lists of Integers ('List Integer' above)              
-}

{-
-- using the Haskell built-in standard lists and notation, the above
-- examples become:

-- tom xs checks whether list xs is empty
tom :: [a] -> Bool
tom []     = True
tom (x:xs) = False

-- forst xs returns the first element of the list xs
forst :: List a -> a
forst []     = error "the list is empty"
forst (x:xs) = x

-- sist xs returns the last element of the list xs
sist :: List a -> a
sist []     = error "the list is empty"
sist [x]    = x
sist (x:xs) = sist xs
-}

{-
-- alternatively:
sist :: List a -> a
sist []     = error "the list is empty"
sist (x:[]) = x
sist (x:xs) = sist xs
-}

--------------------------------------------------------------------

-- summan xs calculates the sum of all elements in xs
summan :: Num a => [a] -> a
summan []     = 0
summan (x:xs) = x + summan xs

{-
The type of 'summan', namely

  summan :: Num a => [a] -> a
  
means that it works for ANY type a, as long as type is a "numeric" or
"Num" type. This means that it works for any type for which we have
the normal arithmetic operations +, -, *, etc.

Note: do a ":i Num" in Hugs and see.
-}

--------------------------------------------------------------------


